package vv.lines.view.swing;

import vv.lines.core.IActionHandler;
import vv.lines.core.ActionId;
import vv.lines.view.View;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/**
 *
 * @author vpotapenko
 */
public class SwingFieldComponent extends JComponent {

    private static final Comparator<SwingFieldItem> itemsComparator =
            new Comparator<SwingFieldItem>() {

        public int compare(SwingFieldItem o1, SwingFieldItem o2) {
            return o1.getZOrder() - o2.getZOrder();
        }
    };

    private ArrayList<SwingFieldItem> items = new ArrayList<SwingFieldItem>();

    private IActionHandler handler;

    private Image fieldImage;

    public SwingFieldComponent() {
        setPreferredSize(new Dimension(400, 400));

        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                onResize();
            }
        });
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                onMouseClick(e);
            }
        });
    }

    void handleDirty(Rectangle... rectangles) {
        Rectangle intersection = new Rectangle();
        Rectangle itemBounds = new Rectangle();

        for (SwingFieldItem item : items) {
            if (item.isDirty()) continue;

            for (Rectangle rect : rectangles) {
                Rectangle.intersect(rect, item.getBounds(itemBounds), intersection);
                if (isValidIntersection(intersection)) {
                    item.setDirty(true);
                    break;
                }
            }
        }
    }

    private boolean hasDirtyItems() {
        for (SwingFieldItem item : items) {
            if (item.isDirty()) return true;
        }
        return false;
    }

    private void initFieldImage() {
        fieldImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        
        Graphics g = fieldImage.getGraphics();
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    private boolean isValidIntersection(Rectangle intersection) {
        return intersection.getWidth() > 0 && intersection.getHeight() > 0;
    }

    private void onMouseClick(MouseEvent e) {
        Point pt = e.getPoint();
        Rectangle bounds = new Rectangle();
        for (int i = items.size() - 1; i >= 0; i--) {
            SwingFieldItem item = items.get(i);
            if (item.getBounds(bounds).contains(pt)) {
                item.onMouseClick(e);
                break;
            }
        }
    }

    private void onResize() {
        if (handler != null) {
            handler.handleAction(ActionId.RESIZE);
        }

        fieldImage = null;
    }

    public void add(View view) {
        if ((view instanceof SwingFieldItem) && !items.contains(view)) {
            SwingFieldItem item = (SwingFieldItem) view;
            item.setOwner(this);
            item.setDirty(true);

            items.add(item);
            updateItemsZOrder();
        }
    }

    public void setHandler(IActionHandler handler) {
        this.handler = handler;
    }

    public void remove(View view) {
        if (view instanceof SwingFieldItem) {
            SwingFieldItem swingObjectView = (SwingFieldItem) view;
            swingObjectView.setOwner(null);
            items.remove(swingObjectView);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        if (hasDirtyItems()) updateFieldImage();

        if (fieldImage != null)
            g.drawImage(fieldImage, getX(), getY(), getWidth(), getHeight(), null);
    }

    public void updateItemsZOrder() {
        Collections.sort(items, itemsComparator);
    }

    private void updateFieldImage() {
        if (fieldImage == null) initFieldImage();

        Graphics g = fieldImage.getGraphics();
        for (SwingFieldItem item : items) {
            if (item.isDirty()) {
                item.paint(g);
                item.setDirty(false);
            }
        }
    }
}
